/*
 * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
 *
 * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
 *
 * Spydroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package net.majorkernelpanic.streaming.hw;

import java.nio.ByteBuffer;

import android.media.MediaCodecInfo;

/**
 * Converts from NV21 to YUV420 semi planar or planar.
 */
public class NV21Convertor {
    private int mSliceHeight, mHeight;
    private int mStride, mWidth;
    private int mSize;
    private boolean mPlanar, mPanesReversed = false;
    private int mYPadding;
    private byte[] mBuffer;
    ByteBuffer mCopy;

    public void setSize(int width, int height) {
        mHeight = height;
        mWidth = width;
        mSliceHeight = height;
        mStride = width;
        mSize = mWidth * mHeight;
    }

    public void setStride(int width) {
        mStride = width;
    }

    public void setSliceHeigth(int height) {
        mSliceHeight = height;
    }

    public void setPlanar(boolean planar) {
        mPlanar = planar;
    }

    public void setYPadding(int padding) {
        mYPadding = padding;
    }

    public int getBufferSize() {
        return 3 * mSize / 2;
    }

    public void setEncoderColorFormat(int colorFormat) {
        switch (colorFormat) {
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
            case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
                setPlanar(false);
                break;
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
                setPlanar(true);
                break;
        }
    }

    public void setColorPanesReversed(boolean b) {
        mPanesReversed = b;
    }

    public int getStride() {
        return mStride;
    }

    public int getSliceHeight() {
        return mSliceHeight;
    }

    public int getYPadding() {
        return mYPadding;
    }


    public boolean getPlanar() {
        return mPlanar;
    }

    public boolean getUVPanesReversed() {
        return mPanesReversed;
    }

    /**
     * 将原始的相机预览数据转换UV通道
     *
     * @param data   要被转换的原始数据
     * @param buffer 用于接收被转换后的数据容器
     */
    public void convert(byte[] data, ByteBuffer buffer) {
        byte[] result = convert(data);
        buffer.put(result, 0, result.length);
    }

    /**
     * 将原始的相机预览数据的UV通道进行转换
     *
     * @param data 这个是原始的相机预览的buffer数据
     * @return 将相机预览原始数据转换之后的数据
     */
    public byte[] convert(byte[] data) {
        // A buffer large enough for every case
        if (mBuffer == null || mBuffer.length != 3 * mSliceHeight * mStride / 2 + mYPadding) {
            mBuffer = new byte[3 * mSliceHeight * mStride / 2 + mYPadding];
        }

        if (!mPlanar) {
            if (mSliceHeight == mHeight && mStride == mWidth) {
                // Swaps U and V
                if (!mPanesReversed) {
                    for (int i = mSize; i < mSize + mSize / 2; i += 2) {
                        mBuffer[0] = data[i + 1];
                        data[i + 1] = data[i];
                        data[i] = mBuffer[0];
                    }
                }
                if (mYPadding > 0) {
                    System.arraycopy(data, 0, mBuffer, 0, mSize);
                    System.arraycopy(data, mSize, mBuffer, mSize + mYPadding, mSize / 2);
                    return mBuffer;
                }
                return data;
            }
        } else {
            if (mSliceHeight == mHeight && mStride == mWidth) {
                // De-interleave U and V
                if (!mPanesReversed) {
                    for (int i = 0; i < mSize / 4; i += 1) {
                        mBuffer[i] = data[mSize + 2 * i + 1];
                        mBuffer[mSize / 4 + i] = data[mSize + 2 * i];
                    }
                } else {
                    for (int i = 0; i < mSize / 4; i += 1) {
                        mBuffer[i] = data[mSize + 2 * i];
                        mBuffer[mSize / 4 + i] = data[mSize + 2 * i + 1];
                    }
                }
                if (mYPadding == 0) {
                    System.arraycopy(mBuffer, 0, data, mSize, mSize / 2);
                } else {
                    System.arraycopy(data, 0, mBuffer, 0, mSize);
                    System.arraycopy(mBuffer, 0, mBuffer, mSize + mYPadding, mSize / 2);
                    return mBuffer;
                }
                return data;
            }
        }

        return data;
    }

    /**
     * 获取默认配置参数的{@link NV21Convertor},主要是用于ShareBuffer使用
     *
     * @return
     */
    public static NV21Convertor getDefaultNV21Convertor() {
        NV21Convertor nv21Convertor = new NV21Convertor();
        nv21Convertor.setSize(640, 480);
        nv21Convertor.setSliceHeigth(240);
        nv21Convertor.setStride(320);
        nv21Convertor.setYPadding(0);
        nv21Convertor.setPlanar(true);
        nv21Convertor.setColorPanesReversed(false);

        return nv21Convertor;
    }

}
